package com.fengye.demo.aspect;

import com.fengye.demo.annotation.Workflow;
import com.fengye.demo.enums.OperationType;
import com.fengye.demo.service.WorkflowService;
import com.fengye.demo.vo.WorkflowVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.EnumUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;

/**
 * @author fengyexjtu@126.com
 * @since 2022-10-05
 */
@Component
@Slf4j
@Aspect
public class WorkflowAspect implements Ordered {
    @Autowired
    private WorkflowService workflowService;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    @Pointcut("execution(* com.fengye.demo.service.IWorkflowTransactionEventService.*(..))")
    public void workflowPointCut() {
    }
    
    @AfterThrowing(pointcut = "workflowPointCut()", throwing = "exc")
    public void afterThrow(JoinPoint joinPoint, Exception exc) {
        log.info("joinPoint.getSignature().getName() => {}", joinPoint.getSignature().getName());
        log.error("joinPoint exception => ", exc);
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        log.error("joinPoint exception method => {}", methodSignature.getMethod());
    }
    
    @Before(value = "workflowPointCut() && @annotation(workflow)")
    public void before(JoinPoint joinPoint, Workflow workflow) throws Throwable {
        log.info("@Before START");
        log.info("@Before workflowVO ===> operation={}", workflow.operation());
        Object[] args = joinPoint.getArgs();
        List<Object> params = Arrays.asList(args);
        log.info("@Before workflowVO ===> 参数列表={}", params);
        WorkflowVO workflowVO = (WorkflowVO) params.stream().findFirst().orElseThrow(() -> new RuntimeException("参数错误"));
        workflowVO.setOperationType(workflow.operation());
        workflowVO.setRecordLog(workflow.isRecordLog());
        log.info("@Before workflowVO ===> 装配 workflowVO 的 operationType和 recordLog 参数={}", workflowVO);
        log.info("@Before END");
    }
    
    @AfterReturning(pointcut = "workflowPointCut() && @annotation(workflow)", returning = "result")
    public void after(JoinPoint joinPoint, Workflow workflow, Object result) {
        log.info("@AfterReturning START");
        WorkflowVO workflowVO = (WorkflowVO) result;
        log.info("@AfterReturning ===> {}", workflowVO);
        OperationType operationType = EnumUtils.getEnum(OperationType.class, String.valueOf(workflow.operation()));
        operationType.event(workflowService, eventPublisher, workflowVO);
        workflowService.recordLog(workflowVO);
        log.info("@AfterReturning ===> {}", workflowVO);
        log.info("@AfterReturning END");
    }
    
    @Override
    public int getOrder() {
        return -1;
    }
    
    @PostConstruct
    public void post(){
        log.info("PostConstruct");
    }
}
